package jt

import (
	"io"
	"os"
	"path/filepath"

	"github.com/dontls/xproto"
	jsoniter "github.com/json-iterator/go"
)

// 查询请求
func (c *Client) doQuery(s interface{}) (uint16, error) {
	v, ok := s.(*xproto.Query)
	if !ok {
		return 0, xproto.ErrParam
	}
	var r jtBytes
	r.addChannel(v.Channels)
	r.addDateTime(v.StartTime)
	r.addDateTime(v.EndTime)
	if v.FileType == xproto.FileAlarmVideo || v.FileType == xproto.FileAlarmVoice {
		r.add64BITS(0xffffffff)
	} else {
		r.add64BITS(0)
	}
	switch v.FileType {
	case xproto.FileNormalVideo, xproto.FileAlarmVideo:
		r.addByte(2)
	// case xproto.FileNormalPic, xproto.FileAlarmPic:
	// 	data.addByte(0)
	case xproto.FileNormalVoice, xproto.FileAlarmVoice:
		r.addByte(1)
	default:
		return 0, xproto.ErrParam
	}
	r.addIntByte(v.Stream)
	r.addIntByte(v.Store)
	return c.sequence, c.WriteMessage(0x9205, r.Bytes)
}

func (c *Client) doParameters(s interface{}) (uint16, error) {
	var p jtParameter
	data, _ := jsoniter.Marshal(s)
	jsoniter.Get(data, "data").ToVal(&p.Data)
	pLen := len(p.Data)
	if pLen == 0 {
		return c.sequence, c.WriteMessage(0x8104, nil)
	}
	r, isGet := jtBytes{}, false
	if p.Data[0].Value == nil {
		isGet = true
	}
	r.addByte(uint8(pLen))
	for _, v := range p.Data {
		id := uint32(hexToUint16(v.ID))
		r.addDWord(id)
		if !isGet && v.Value != nil {
			p.Parse(id, &r, v.Value)
		}
	}
	if isGet {
		return c.sequence, c.WriteMessage(0x8106, r.Bytes)
	}
	return 0, c.WriteMessage(0x8103, r.Bytes)
}

func (c *Client) doLiveStream(s interface{}) (uint16, error) {
	v, ok := s.(*xproto.LiveStream)
	if !ok || v.Server == "" {
		return 0, xproto.ErrParam
	}
	var r jtBytes
	// 关闭视频
	if v.On == 0 {
		r.addIntByte(v.Channel)
		r.addByte(0)
		r.addByte(0)
		r.addIntByte(v.Stream)
		return c.sequence, c.WriteMessage(0x9102, r.Bytes)
	}
	if err := r.addTcpServer(v.Server); err != nil {
		return 0, err
	}
	r.addWord(0)
	r.addIntByte(v.Channel)
	r.addByte(0)
	r.addIntByte(v.Stream)
	return c.sequence, c.WriteMessage(0x9101, r.Bytes)
}

// 表 24 (续)
func (c *Client) doPlayback(s interface{}) (uint16, error) {
	v, ok := s.(*xproto.Playback)
	if !ok || v.Server == "" {
		return 0, xproto.ErrParam
	}

	var r jtBytes
	if err := r.addTcpServer(v.Server); err != nil {
		return 0, err
	}
	r.addWord(0) // udp 端口号
	if v.FileName != "" {
		var p jtRecFile
		if _, err := p.parseFileName(v.FileName); err != nil {
			return 0, xproto.ErrParam
		}
		r.addByte(p.Chl)
		r.addByte(p.Frame)  // 0：音视频，1：音频，2：视频，3：视频或音视频
		r.addByte(p.Stream) // 0：主码流或子码流，1：主码流，2：子码流；如果此通道只传输音频，此字段置 0
		r.addByte(p.Store)  // 0：主存储器或灾备存储器，1：主存储器，2：灾备存储器
		r.addByte(0)        // 0：正常回放；1：快进回放；2：关键帧快退回放；3：关键帧播放；4：单帧上传
		r.addByte(0)        // 回放方式为 1 和 2 时，此字段内容有效，否则置 0. 0：无效；1：1 倍；2：2 倍 3：4 倍；4：8 倍；5：16 倍
		r.addDateTime(p.StartTime)
		r.addDateTime(p.EndTime)
	} else {
		r.addChannel(v.Channels)
		r.addByte(0) // 0：音视频，1：音频，2：视频，3：视频或音视频
		r.addByte(0) // 0：主码流或子码流，1：主码流，2：子码流；如果此通道只传输音频，此字段置 0
		r.addByte(0) // 0：主存储器或灾备存储器，1：主存储器，2：灾备存储器
		r.addByte(0) // 0：正常回放；1：快进回放；2：关键帧快退回放；3：关键帧播放；4：单帧上传
		r.addByte(0) // 回放方式为 1 和 2 时，此字段内容有效，否则置 0. 0：无效；1：1 倍；2：2 倍 3：4 倍；4：8 倍；5：16 倍
		r.addDateTime(v.StartTime)
		r.addDateTime(v.EndTime)
	}
	return c.sequence, c.WriteMessage(0x9201, r.Bytes) // 这里采用0x1205作为应答
}

// doFileTransfer 文件传输
func (c *Client) doFileTransfer(s interface{}) (uint16, error) {
	v, ok := s.(*xproto.FileTransfer)
	if !ok {
		return 0, xproto.ErrParam
	}
	var r jtBytes
	if v.Action == xproto.ActUpload && v.FileType == xproto.FileUpgrade {
		fp, err := os.Open(v.FileName)
		if err != nil {
			return 0, err
		}
		defer fp.Close()
		fileName := filepath.Base(v.FileName)
		r.addByte(0) // TODO判断类型
		r.addBytes(c.terminal.ManufacturerID)
		r.addString(fileName, 1)
		fi, _ := fp.Stat()
		r.addDWord(uint32(fi.Size()))
		bs := make([]byte, 10*1024)
		for {
			s, err := fp.Read(bs)
			if err == io.EOF {
				break
			}
			r.addBytes(bs[:s])
		}
		// 升级包数据
		if err := c.WriteMessage(0x8108, r.Bytes); err != nil {
			return 0, err
		}
		c.key.Upgrade = c.sequence
		return c.key.Upgrade, nil
	}
	return 0, xproto.ErrParam
}

// doFtpTransfer Ftp文件传输
func (c *Client) doFtpTransfer(s interface{}) (uint16, error) {
	v, ok := s.(*xproto.FtpTransfer)
	if !ok {
		return 0, xproto.ErrParam
	}
	var p jt0x9206
	if err := p.parseFileName(v.FileSrc); err != nil {
		return 0, err
	}
	if err := p.parseFtpUrl(v.FtpURL); err != nil {
		return 0, err
	}
	var r jtBytes
	r.addString(p.Addr, 1)
	r.addWord(p.Port)
	r.addString(p.Usernmae, 1)
	r.addString(p.Password, 1)
	r.addString(v.FileDst, 1)
	r.addByte(p.Chl)
	r.addDateTime(p.StartTime)
	r.addDateTime(p.EndTime)
	r.add64BITS(uint64(p.AlarmFlag))
	r.addByte(p.Frame)
	r.addByte(p.Stream)
	r.addByte(p.Store)
	r.addByte(p.Net)
	return c.sequence, c.WriteMessage(0x9206, r.Bytes)
}

// 语音
func (c *Client) doLiveControl(s interface{}) (uint16, error) {
	return 0, xproto.ErrUnSupport
}

//{"codeId": "0x8107", "data": {}}

func (c *Client) doUser(s interface{}) (uint16, error) {
	u, ok := s.(*xproto.User)
	if !ok {
		return 0, xproto.ErrParam
	}
	codeID := hexToUint16(u.CodeID)
	switch codeID {
	case 0x8201:
		return c.sequence, c.WriteMessage(codeID, nil)
	case 0x8107:
		if err := c.WriteMessage(codeID, nil); err != nil {
			return 0, err
		}
		c.key.Property = c.sequence
		return c.key.Property, nil
	case 0x9003: // 0x9003音视频属性
		if err := c.WriteMessage(codeID, nil); err != nil {
			return 0, err
		}
		c.key.AVProperty = c.sequence
		return c.key.AVProperty, nil
	}
	sdata, _ := jsoniter.Marshal(s)
	jval := jsoniter.Get(sdata, "data")
	if err := jval.LastError(); err != nil {
		return 0, err
	}
	var r jtBytes
	switch codeID {
	case 0x8202:
		var p Jt8202
		jval.ToVal(&p)
		r.addWord(p.Interval)
		r.addDWord(p.EffectiveTime)
	case 0x8203:
		var p Jt8203
		jval.ToVal(&p)
		r.addWord(p.AlarmSerial)
		r.addDWord(p.AlarmType)
	case 0x8302:
		var p Jt8302
		jval.ToVal(&p)
		r.addByte(p.Flag)
		r.addString(p.Question, 1)
		for _, v := range p.Answers {
			r.addByte(v.Id)
			r.addString(v.Content, 1)
		}
		if err := c.WriteMessage(codeID, r.Bytes); err != nil {
			return 0, err
		}
		c.key.OnDemand = c.sequence
		return c.key.OnDemand, nil
	case 0x8303:
		var p Jt8303
		jval.ToVal(&p)
		r.addByte(p.Type)
		r.addByte(uint8(len(p.Message)))
		for _, v := range p.Message {
			r.addByte(v.Type)
			r.addString(v.Name, 2)
		}
	case 0x8304:
		var p Jt8304
		jval.ToVal(&p)
		r.addByte(p.Type)
		r.addString(p.Content, 2)
	case 0x8400:
		var p Jt8400
		jval.ToVal(&p)
		r.addByte(p.Type)
		r.addString(p.PhoneNumber, 0) // TODO check max 20
	case 0x8401:
		var p Jt8401
		jval.ToVal(&p)
		r.addByte(p.Type)
		r.addByte(uint8(len(p.Contacts)))
		for _, v := range p.Contacts {
			r.addByte(v.Type)
			r.addString(v.PhoneNumber, 1)
			r.addString(v.Name, 1)
		}
	case 0x8600:
		var p Jt8600
		jval.ToVal(&p)
		r.addByte(p.Action)
		r.addByte(uint8(len(p.Regions)))
		for _, v := range p.Regions {
			r.addDWord(v.ID)
			r.addWord(v.Attr)
			r.addDWord(uint32(v.La * 1000000))
			r.addDWord(uint32(v.Lo * 1000000))
			r.addDWord(v.Ra)
			r.addDateTime(v.St)
			r.addDateTime(v.Et)
			r.addWord(v.MaxSpd)
			r.addByte(v.KeepTime)
			r.addWord(v.MaxNightSpd)
			r.addString(v.Name, 2)
		}
	case 0x8602:
		var p Jt8602
		jval.ToVal(&p)
		r.addByte(p.Action)
		r.addByte(uint8(len(p.Regions)))
		for _, v := range p.Regions {
			r.addDWord(v.ID)
			r.addWord(v.Attr)
			r.addDWord(uint32(v.LeftTopLa * 1000000))
			r.addDWord(uint32(v.LeftTopLo * 1000000))
			r.addDWord(uint32(v.RightBotLa * 1000000))
			r.addDWord(uint32(v.RightBotLo * 1000000))
			r.addDateTime(v.St)
			r.addDateTime(v.Et)
			r.addWord(v.MaxSpd)
			r.addByte(v.KeepTime)
			r.addWord(v.MaxNightSpd)
			r.addString(v.Name, 2)
		}
	case 0x8604:
		var p Jt8604
		jval.ToVal(&p)
		r.addDWord(p.ID)
		r.addWord(p.Attr)
		r.addDateTime(p.St)
		r.addDateTime(p.Et)
		r.addWord(p.MaxSpd)
		r.addByte(p.KeepTime)
		r.addWord(uint16(len(p.Dots)))
		for _, v := range p.Dots {
			r.addDWord(uint32(v.La * 1000000))
			r.addDWord(uint32(v.Lo * 1000000))
		}
		r.addWord(p.MaxNightSpd)
		r.addString(p.Name, 2)
	case 0x8601, 0x8603, 0x8605:
		var p Jt8601O3
		jval.ToVal(&p)
		r.addByte(uint8(len(p.IDs)))
		for _, v := range p.IDs {
			r.addDWord(v)
		}
	case 0x9105: // 实时音视频传输状态通知
		var p jt0x9105
		jval.ToVal(&p)
		r.addByte(p.Channel)
		r.addByte(p.LossRate)
	default:
		return 0, nil
	}
	return c.sequence, c.WriteMessage(codeID, r.Bytes)
}

func (c *Client) doControl(s interface{}) (uint16, error) {
	v, ok := s.(*xproto.Control)
	if !ok {
		return 0, xproto.ErrParam
	}
	data, _ := jsoniter.Marshal(v.Data)
	j := jsoniter.Get(data)
	var r jtBytes
	switch v.Type {
	case xproto.CtrlText:
		var p Jt8300
		if err := jsoniter.Unmarshal(data, &p); err != nil {
			return 0, err
		}
		r.addByte(p.Flag)
		r.addString(p.Text, 0)
		return c.sequence, c.WriteMessage(0x8300, r.Bytes)
	case xproto.CtrlVehi:
		code := xproto.VehiCode(j.Get("code").ToInt())
		if code == xproto.VehiOpenDoor {
			r.addByte(0x00)
		} else if code == xproto.VehiCloseDoor {
			r.addByte(0x01)
		} else {
			return 0, xproto.ErrParam
		}
		return c.sequence, c.WriteMessage(0x8500, r.Bytes)
	case xproto.CtrlReset:
		r.addByte(4)
		return 0, c.WriteMessage(0x8105, r.Bytes)
	case xproto.CtrlSnapshot:
		var p xproto.ControlSnapshot
		j.ToVal(&p)
	case xproto.CtrlLive:
		var p xproto.ControlLive
		j.ToVal(&p)
		r.addByte(p.Channel)
		r.addByte(p.Type)
		r.addByte(p.CloseMeidaType)
		r.addByte(p.ToggleStreamType)
		return 0, c.WriteMessage(0x9102, r.Bytes)
	}
	return 0, nil
}

// doVoice 语音业务
func (c *Client) doVoice(s interface{}) (uint16, error) {
	v, ok := s.(*xproto.Voice)
	if !ok || v.Server == "" {
		return 0, xproto.ErrParam
	}
	var r jtBytes
	if err := r.addTcpServer(v.Server); err != nil {
		return 0, err
	}
	r.addWord(0)
	r.addIntByte(v.Channel)
	if v.WorkMode == 0 {
		r.addByte(3)
	} else if v.WorkMode == 1 {
		r.addByte(2)
	} else if v.WorkMode == 2 {
		r.addByte(4)
	}
	r.addByte(0)
	return c.sequence, c.WriteMessage(0x9101, r.Bytes)
}
